'''
Converts a Sourceforge svnnofiy e-mail notification into a SourceChangeNotification.
'''
import re
import datetime
import time

_PATTERN = re.compile( r"""
Revision:\s*([0-9]+)\s+.*?
Author:\s*([^\n]+)\s*?
Date:\s*([^ ]+\ [^ ]+\ [^ ]+)
""", re.DOTALL | re.VERBOSE )


_SECTION_KINDS = {
    'Modified Paths': 'modified',
    'Added Paths': 'added',
    'Removed Paths': 'removed',
    'Log Message': 'comment'
    }

def _extract_section_kind( line ):
    end_index = line.find(':')
    if end_index > 0:
        kind = line[0:end_index]
        return _SECTION_KINDS.get(kind)
    return None

def _strip_blank_lines( comment_lines ):
    """Strips head and tail blank lines."""
    while comment_lines and not comment_lines[0].strip():
        del comment_lines[0]
    while comment_lines and not comment_lines[-1].strip():
        del comment_lines[-1]
    return comment_lines


def convert( mail ):
    """Converts a PendingMail into a source change notification.
    Parameters:
        mail: a PendingMail entity (or similar object)
    Returns:
        A dict with the following keys:
        'author': Author
        'revision': SVN revision
        'date': a datetime corresponding to moment of the change
        'message': comment provided with the change
        'paths_count': total number of added/modified/removed paths
        'added': List of added paths
        'modified': List of modified paths
        'removed': List of removed paths
    """
    body = mail.body.replace('\r\n','\n').replace('\r','\n')
    m = _PATTERN.search( body )
    if m is None:
        #logging.error( 'Failed to parse svnnotify mail to %s' % to_project )
        return None
    revision, author, datetime_text = m.groups()
#    print 'Revision:', revision
#    print 'Author:', author
    #print '\n'.join( unicode(s) for s in m.groups() )
    # Date format: 2010-03-11 20:23:07 +0000 (Thu, 11 Mar 2010)
    date = datetime.datetime.strptime( datetime_text, "%Y-%m-%d %H:%M:%S +0000" )
#    print 'Date:', date
    changes_by_kind = {}
    clean_kinds = {'Modified Paths': 'modified',
                   'Added Paths': 'added',
                   'Removed Paths': 'removed',
                   'Log Message': 'comment'}
    current_kind = None
    skip_next = False
    # @todo: fetch comment
    for line in body.split('\n'):
        if skip_next:
            skip_next = False
            continue
        clean_kind = _extract_section_kind( line )
        if clean_kind:
            current_kind = clean_kind
            assert clean_kind not in changes_by_kind
            changes_by_kind[clean_kind] = []
            skip_next = True
        elif current_kind == 'comment':
            changes_by_kind[current_kind].append( line.rstrip() )
        elif current_kind and line[0:4] == '    ':
            changes_by_kind[current_kind].append( line.strip() )
#    for kind, paths in changes_by_kind.iteritems():
#        print kind, ':'
#        print '\n'.join( paths )
    comment_lines = _strip_blank_lines( changes_by_kind.get( 'comment', [] ) )
    comment = '\n'.join( comment_lines )
    paths_count = ( len(detail['added']) + len(detail['modified']) +
                    len(detail['removed']) )
    timestamp = time.mktime( date.utctimetuple() )
    return {
        'revision': revision,
        'author': author,
        'timestamp': timestamp,
        'message': comment,
        'paths_count': paths_count,
        'added': changes_by_kind.get( 'added', [] ),
        'modified': changes_by_kind.get( 'modified', [] ),
        'removed': changes_by_kind.get( 'removed', [] )
        }

if __name__ == '__main__':
    import unittest
    import pickle
    from webbuildbot.models.mail import PendingMail

    def dict2entity( d ):
        e = PendingMail()
        for key, value in d.iteritems():
            setattr( e, key, value )
        return e

    def dump_change( change ):
        from pprint import pprint
        pprint( change )

    def main():
        mails = pickle.loads( open( 'svnnotify_mail.pickle', 'rb' ).read() )
        mails = [dict2entity(d) for d in mails]
#        dump_change( convert( mails[3] ) )
        for index, mail in enumerate(mails):
            print
            print '#%d:' % index
            dump_change( convert( mail ) )

    main()